task
众所周知,二叉树是一种最多含有2个子节点的树形数据结构。
现在有一棵无限大的二叉树。在这棵树中,每个节点都有左右两个儿子,和一个父亲。我们称根节点的父亲是它自己。kxj极其无聊地将手指指向根节点,然后极其无聊地根据指令S移动手指。
一串指令S为一个字符串,这个字符串是包含大写的L,R,U分别表示着走向当前节点的左儿子,右儿子和父亲。
现在kxj又得到了另一串指令L,这一串指令从指令S结束时手指所在的节点开始移动。但是这一次kxj可以跳掉T中的任意指令(可能一个都不跳,或者全跳)。kxj想知道最终他的手指可以指向多少个不同的节点。
S,T的长度<=100000
solution
首先我们将指令S处理一下,因为在这一条指令中是有许多无效操作,即‘U’操作,每次遇到一个‘U’就意味着往上走,那么我们可以发现我们又退回到上一个点了,那么这之间的操作都是无效的。这样我们最后处理完的指令中只有L和R,这一条指令就在二叉树中构成一条向下的路径。
那么接下来我们考虑在每个深度向下走,在初始深度我们可以使用T中的所有的L和R,用这些L和R构成的不同子序列都代表着一个不同的结果。而当我们每用一个‘U’,我们都可以上去一层(root除外),这时候我们只能往另一个儿子那边走,即如果初始点是一个左儿子,那么我们我们在我们可以用的L和R中找所有一个R开头的不同的子序列。
所以其实题目就变成了在一个只有L和R的字符串中找到所有不同的子序列。
对于这种题,我们可以借用trie树的思路,trie树上有几个节点就代表有多少的子序列。
而trie树中为R的节点都代表着一个以R结尾的字符串。L同理。
所以我们就枚举指令T,每次将trie树中一个与当前字符有关的字符串删去,然后遇到一个‘U’,就将加上此时trie树中的‘L’节点或者‘R’节点。
但是有一个非常严肃的问题,我们无法做到在trie树上删点。
那么根据通常的思路我们改删点为加点即可,倒着枚举指令T,然后我们就可以将每个非‘U’的字符加到当前trie树上的节点上(前提是该节点的儿子中没有这种字符)。
而且实际上我们没有必要将trie数构出来,因为我们只是想知道trie树中L和R节点的个数而已。所以我们只要维护5个参数L,R,Lcnt,Rcnt,leaf 就好了。
L和R分别表示trie树中L节点和R节点的个数,Lcnt和Rcnt分别表示在trie树中有多少节点是只有一个右儿子(即左儿子是空的)或只有一个左儿子(即右儿子是空的),leaf就是叶子节点。
每当我们枚举到一个‘L’,那么此时trie树上就有leaf+Lcnt个节点可以加一个左儿子。那么几个参数的更新如下:
$L+=leaf+Lcnt$
$Rcnt+=leaf$
$leaf+=Lcnt$
$Lcnt=0$
枚举到‘R’同理。
如果枚举到‘U’呢?假设这是正数第x个‘U’,那么我们就要看在新的S指令中的正数第x个字符什么,如果是‘L’,那么我们就为答案加上一个R+1,反之同理。
但是当x大于S中字符的个数是,我们不用更新答案。
最后我们还要加上一个$L+R+1$,表示在初始节点时向下走的方案数。
Code
1 |
|